home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / utils / exec33a.arj / EXEC.PAS < prev    next >
Pascal/Delphi Source File  |  1993-06-22  |  36KB  |  1,182 lines

  1. Unit exec;
  2. {  --- Version 3.3 93-06-22 14:45 ---
  3.  
  4.    EXEC.PAS: EXEC function with memory swap - prepare parameters.
  5.  
  6.    Needs Assembler file 'spawn.asm' (assembled as 'spawnp.obj')
  7.    and unit 'checkpat'.
  8.  
  9. Public domain software by
  10.  
  11.         Thomas Wagner
  12.         Ferrari electronic GmbH
  13.         Beusselstrasse 27
  14.         D-1000 Berlin 21
  15.         West Germany
  16.  
  17.         BIXname: twagner
  18. }
  19.  
  20. Interface
  21.  
  22. Uses
  23.   Dos, checkpat;
  24.  
  25. const
  26.  
  27. {e Return codes (only upper byte significant) }
  28. {d Fehlercodes (nur das obere Byte signifikant) }
  29.  
  30.    RC_PREPERR   = $0100;
  31.    RC_NOFILE    = $0200;
  32.    RC_EXECERR   = $0300;
  33.    RC_ENVERR    = $0400;
  34.    RC_SWAPERR   = $0500;
  35.    RC_REDIRERR  = $0600;
  36.  
  37. {e Swap method and option flags }
  38. {d Auslagerungsmethoden ond Optionen }
  39.  
  40.    USE_EMS      =  $01;
  41.    USE_XMS      =  $02;
  42.    USE_FILE     =  $04;
  43.    EMS_FIRST    =  $00;
  44.    XMS_FIRST    =  $10;
  45.    HIDE_FILE    =  $40;
  46.    NO_PREALLOC  = $100;
  47.    CHECK_NET    = $200;
  48.  
  49.    USE_ALL      = USE_EMS or USE_XMS or USE_FILE or CHECK_NET;
  50.  
  51.  
  52. type
  53.     filename = string [81];
  54.     string128 = string [128];
  55.     pstring = ^string;
  56.  
  57.  
  58. function do_exec (xfn: string; pars: string; spawn: integer;
  59.                   needed: word; newenv: boolean): integer;
  60.  
  61.    {>e
  62.       The EXEC function.
  63.  
  64.       Parameters:
  65.  
  66.          xfn      is a string containing the name of the file
  67.                   to be executed. If the string is empty,
  68.                   the COMSPEC environment variable is used to
  69.                   load a copy of COMMAND.COM or its equivalent.
  70.                   If the filename does not include a path, the
  71.                   current PATH is searched after the default.
  72.                   If the filename does not include an extension,
  73.                   the path is scanned for a COM, EXE, or BAT file 
  74.                   in that order.
  75.  
  76.          pars     The program parameters.
  77.  
  78.          spawn    If 0, the function will terminate after the 
  79.                   EXECed program returns, the function will not return.
  80.  
  81.                   NOTE: If the program file is not found, the function
  82.                         will always return with the appropriate error 
  83.                         code, even if 'spawn' is 0.
  84.  
  85.                   If non-0, the function will return after executing the
  86.                   program. If necessary (see the "needed" parameter),
  87.                   memory will be swapped out before executing the program.
  88.                   For swapping, spawn must contain a combination of the
  89.                   following flags:
  90.  
  91.                      USE_EMS  ($01)  - allow EMS swap
  92.                      USE_XMS  ($02)  - allow XMS swap
  93.                      USE_FILE ($04)  - allow File swap
  94.  
  95.                   The order of trying the different swap methods can be
  96.                   controlled with one of the flags
  97.  
  98.                      EMS_FIRST ($00) - EMS, XMS, File (default)
  99.                      XMS_FIRST ($10) - XMS, EMS, File
  100.  
  101.                   If swapping is to File, the attribute of the swap file
  102.                   can be set to "hidden", so users are not irritated by
  103.                   strange files appearing out of nowhere with the flag
  104.  
  105.                      HIDE_FILE ($40) - create swap file as hidden
  106.  
  107.                   and the behaviour on Network drives can be changed with
  108.  
  109.                      NO_PREALLOC (0x100) - don't preallocate
  110.                      CHECK_NET (0x200)   - don't preallocate if file on net.
  111.  
  112.                   This checking for Network is mainly to compensate for
  113.                   a strange slowdown on Novell networks when preallocating
  114.                   a file. You can either set NO_PREALLOC to avoid allocation
  115.                   in any case, or let the prep_swap routine decide whether
  116.                   to do preallocation or not depending on the file being
  117.                   on a network drive (this will only work with DOS 3.1 or 
  118.                   later).
  119.  
  120.          needed   The memory needed for the program in paragraphs (16 Bytes).
  121.                   If not enough memory is free, the program will
  122.                   be swapped out.
  123.                   Use 0 to never swap, $ffff to always swap. 
  124.                   If 'spawn' is 0, this parameter is irrelevant.
  125.  
  126.          newenv   If this parameter is FALSE, the environment
  127.                   of the spawned program is a copy of the parent's
  128.                   environment. If it is TRUE, a new environment
  129.                   is created which includes the modifications from
  130.                   previous 'putenv' calls.
  131.  
  132.       Return value:
  133.  
  134.          $0000..00FF: The EXECed Program's return code
  135.  
  136.          $0101:       Error preparing for swap: no space for swapping
  137.          $0102:       Error preparing for swap: program too low in memory
  138.  
  139.          $0200:       Program file not found
  140.          $0201:       Program file: Invalid drive
  141.          $0202:       Program file: Invalid path
  142.          $0203:       Program file: Invalid name
  143.          $0204:       Program file: Invalid drive letter
  144.          $0205:       Program file: Path too long
  145.          $0206:       Program file: Drive not ready
  146.          $0207:       Batchfile/COMMAND: COMMAND.COM not found
  147.          $0208:       Error allocating temporary buffer
  148.  
  149.          $03xx:       DOS-error-code xx calling EXEC
  150.  
  151.          $0400:       Error allocating environment buffer
  152.  
  153.          $0500:       Swapping requested, but prep_swap has not 
  154.                        been called or returned an error.
  155.          $0501:       MCBs don't match expected setup
  156.          $0502:       Error while swapping out
  157.  
  158.          $0600:       Redirection syntax error
  159.          $06xx:       DOS error xx on redirection
  160.    <}
  161.  
  162.    {>d
  163.       Die EXEC Funktion.
  164.  
  165.       Parameter:
  166.  
  167.          xfn      ist ein String mit dem Namen der auszuführenden Datei.
  168.                   Ist der String leer, wird die COMSPEC Umgebungsvariable
  169.                   benutzt um COMMAND.COM oder das Equivalent zu laden.
  170.                   Ist kein Pfad angegeben, wird nach dem aktuellen Pfad
  171.                   der in der PATH Umgebungsvariablen angegebene Pfad
  172.                   durchsucht.
  173.                   Ist kein Dateityp angegeben, wird der Pfad nach
  174.                   einer COM oder EXE Datei (in dieser Reihenfolge) abgesucht.
  175.  
  176.          pars     Die Kommandozeile
  177.  
  178.          spawn    Wenn 0, wird der Programmlauf beendet wenn das
  179.                   aufgerufene Programm zurückkehrt, die Funktion kehrt
  180.                   nicht zurück.
  181.  
  182.                   HINWEIS: Wenn die auszuführende Datei nicht gefunden
  183.                         wird, kehrt die Funktion mit einem Fehlercode
  184.                         zurück, auch wenn der 'spawn' Parameter 0 ist.
  185.  
  186.                   Wenn nicht 0, kehrt die Funktion nach Ausführung des
  187.                   Programms zurück. Falls notwendig (siehe den Parameter
  188.                   "needed") wird der Programmspeicherbereich vor Aufruf
  189.                   ausgelagert.
  190.                   Zur Auslagerung muß der Parameter eine Kombination der
  191.                   folgenden Flags enthalten:
  192.  
  193.                      USE_EMS  ($01)  - Auslagerung auf EMS zulassen
  194.                      USE_XMS  ($02)  - Auslagerung auf XMS zulassen
  195.                      USE_FILE ($04)  - Auslagerung auf Datei zulassen
  196.  
  197.                   Die Reihenfolge der Versuche, auf die verschiedenen
  198.                   Medien auszulagern kann mit einem der folgenden
  199.                   Flags beeinflußt werden:
  200.  
  201.                      EMS_FIRST ($00) - EMS, XMS, Datei (Standard)
  202.                      XMS_FIRST ($10) - XMS, EMS, Datei
  203.  
  204.                   Wenn die Auslagerung auf Datei erfolgt, kann das
  205.                   Attribut dieser Datei auf "hidden" gesetzt werden,
  206.                   damit der Benutzer nicht durch unversehends auftauchende
  207.                   Dateien verwirrt wird:
  208.  
  209.                      HIDE_FILE ($40) - Auslagerungsdatei "hidden" erzeugen
  210.  
  211.                   Außerdem kann das Verhalten auf Netzwerk-Laufwerken 
  212.                   beeinflußt werden mit
  213.  
  214.                      NO_PREALLOC (0x100) - nicht Präallozieren
  215.                      CHECK_NET (0x200)   - nicht Präallozieren wenn Netz.
  216.  
  217.                   Diese Prüfung auf Netzwerk ist hauptsächlich sinnvoll
  218.                   für Novell Netze, bei denen eine Präallozierung eine
  219.                   erhebliche Verzögerung bewirkt. Sie können entweder mit
  220.                   NO_PREALLOC eine Präallozierung in jedem Fall ausschließen,
  221.                   oder die Entscheidung mit CHECK_NET prep_swap überlassen.
  222.                   In diesem Fall wird nicht präalloziert wenn die Datei
  223.                   auf einem Netzwerk-Laufwerk liegt (funktioniert nur
  224.                   mit DOS Version 3.1 und späteren).
  225.  
  226.          needed   Der zur Ausführung des Programms benötigte Speicher
  227.                   in Paragraphen (16 Bytes). Wenn nicht ausreichend 
  228.                   freier Speicher vorhanden ist, wird der Programm-
  229.                   speicherbereich ausgelagert.
  230.                   Bei Angabe von 0 wird nie ausgelagert, bei Angabe
  231.                   von $ffff wird immer ausgelagert.
  232.                   Ist der Parameter 'spawn' 0, hat 'needed' keine Bedeutung.
  233.  
  234.          newenv   Bestimmt die dem gerufenen Programm zu übergebenden 
  235.                   Umgebungsvariablen. Ist der Parameter FALSE,
  236.                   wird eine Kopie der Vater-Umgebung benutzt,
  237.                   d.h. daß Aufrufe von "putenv" keinen Effekt haben.
  238.                   Ist er TRUE, wird eine neue Umgebung mit den 
  239.                   Modifikationen aus 'putenv' übergeben.
  240.  
  241.       Liefert:
  242.  
  243.          $0000..00FF: Rückgabewert des aufgerufenen Programms
  244.  
  245.          $0101:       Fehler bei Vorbereitung zum Auslagern -
  246.                        kein Speicherplatz in XMS/EMS/Datei
  247.          $0102:       Fehler bei Vorbereitung zum Auslagern -
  248.                        der Programmcode ist zu nah am Beginn des
  249.                        Programms.
  250.  
  251.          $0200:       Auszuführende Programmdatei nicht gefunden
  252.          $0201:       Programmdatei: Ungültiges Laufwerk
  253.          $0202:       Programmdatei: Ungültiger Pfad
  254.          $0203:       Programmdatei: Ungültiger Dateiname
  255.          $0204:       Programmdatei: Ungültiger Laufwerksbuchstabe
  256.          $0205:       Programmdatei: Pfad zu lang
  257.          $0206:       Programmdatei: Laufwerk nicht bereit
  258.          $0207:       Batchfile/COMMAND: COMMAND.COM nicht gefunden
  259.          $0208:       Fehler beim allozieren eines temporären Puffers
  260.  
  261.          $03xx:       DOS-Fehler-Code xx bei Aufruf von EXEC
  262.  
  263.          $0400:       Fehler beim allozieren der Umgebungsvariablenkopie
  264.  
  265.          $0500:       Auslagerung angefordert, aber prep_swap wurde nicht
  266.                        aufgerufen oder lieferte einen Fehler
  267.          $0501:       MCBs entsprechen nicht dem erwarteten Aufbau
  268.          $0502:       Fehler beim Auslagern
  269.  
  270.          $0600:      Redirection Syntaxfehler
  271.          $06xx:      DOS-Fehler xx bei Redirection
  272.    <}
  273.  
  274. {>e
  275.    The function pointed to by "spawn_check" will be called immediately 
  276.    before doing the actual swap/exec, provided that
  277.  
  278.       - the preparation code did not detect an error, and
  279.       - "spawn_check" is not NIL.
  280.  
  281.    The function definition is
  282.       function name (cmdbat: integer; swapping: integer; var execfn: string; 
  283.                      var progpars: string): integer;
  284.  
  285.    The parameters passed to this function are
  286.  
  287.       cmdbat      1: Normal EXE/COM file
  288.                   2: Executing BAT file via COMMAND.COM
  289.                   3: Executing COMMAND.COM (or equivalent)
  290.  
  291.       swapping    < 0: Exec, don't swap
  292.                     0: Spawn, don't swap
  293.                   > 0: Spawn, swap
  294.  
  295.       execfn      the file name to execute (complete with path)
  296.  
  297.       progpars    the program parameter string
  298.  
  299.    If the routine returns anything other than 0, the swap/exec will
  300.    not be executed, and do_exec will return with this code.
  301.  
  302.    You can use this function to output messages (for example, the
  303.    usual "enter EXIT to return" message when loading COMMAND.COM)
  304.    and to do clean-up and additional checking.
  305.  
  306.    CAUTION: If swapping is > 0, the routine may not modify the 
  307.    memory layout, i.e. it may not call any memory allocation or
  308.    deallocation routines.
  309.  
  310.    "spawn_check" is initialized to NIL.
  311. <}
  312. {>d
  313.    Die Funktion auf die "spawn_check" zeigt wird unmittelbar vor
  314.    Ausführung des Programmaufrufs aufgerufen, vorausgesetzt daß
  315.  
  316.       - bei der Vorbereitung kein Fehler auftrat, und
  317.       - "spawn_check" nicht NIL ist.
  318.  
  319.    Die Funktionsdefinition ist
  320.       function name (cmdbat: integer; swapping: integer; var execfn: string; 
  321.                      var progpars: string): integer;
  322.  
  323.    Die der Funktion übergebenen Parameter sind
  324.  
  325.       cmdbat      1: Normale EXE/COM Datei
  326.                   2: Ausführung BAT Datei über COMMAND.COM
  327.                   3: Ausführung COMMAND.COM (oder Equivalent)
  328.  
  329.       swapping    < 0: Exec, keine Auslagerung
  330.                     0: Spawn, keine Auslagerung
  331.                   > 0: Spawn, Auslagern
  332.  
  333.       execfn      Name und Pfad der auszuführenden Datei
  334.  
  335.       progpars    Programmparameter
  336.  
  337.    Wenn die Routine einen Wert verschieden von 0 liefert, wird der
  338.    Programmaufruf nicht durchgeführt, und do_exec kehrt mit diesem
  339.    Wert zurück.
  340.  
  341.    Sie können diese Funktion benutzen um Meldungen auszugeben
  342.    (zum Beispiel die übliche Meldung "Geben Sie EXIT ein um 
  343.    zurückzukehren" bei Laden von COMMAND.COM), und für sonstige
  344.    Prüfungen oder Aufräumarbeiten.
  345.  
  346.    ACHTUNG: Wenn swapping > 0 ist, darf die Funktion keinesfalls 
  347.    den Speicheraufbau verändern, d.h. es dürfen keine Speicher-
  348.    Allozierungs oder -Deallozierungsroutinen benutzt werden.
  349.  
  350.    "spawn_check" ist auf NIL initialisiert.
  351. <}
  352.  
  353. type
  354.    spawn_check_proc = function (cmdbat: integer; swapping: integer; 
  355.                                 var execfn: string; var progpars: string)
  356.                                : integer;
  357. var
  358.    spawn_check: spawn_check_proc;
  359.  
  360. {>e
  361.    The 'swap_prep' variable can be accessed from the spawn_check
  362.    call-back routine for additional information on the nature and
  363.    parameters of the swap. This variable will ONLY hold useful
  364.    information if the 'swapping' parameter to spawn_check is > 0.
  365.    The contents of this variable may not be changed.
  366.  
  367.    The 'swapmethod' field will contain one of the flags USE_FILE, 
  368.    USE_XMS, or USE_EMS.
  369.  
  370.    Note that the 'swapfilename' field contains a zero-terminated string
  371.    with no prefixed length byte, not a Pascal string.
  372. <}
  373. {>d
  374.    Die Variable 'swap_prep' kann von der spawn_check Routine
  375.    benutzt werden um zusätzliche Informationen über Art und Parameter
  376.    der Auslagerung zu erfahren. Diese Variable enthält NUR DANN 
  377.    sinnvolle Werte wenn der 'swapping' Parameter von spawn_check > 0 ist.
  378.    Der Inhalt dieser Variablen darf keinesfalls verändert werden.
  379.  
  380.    Das Feld 'swapmethod' enthält einen der Werte USE_FILE, 
  381.    USE_XMS, oder USE_EMS.
  382.  
  383.    Bitte beachten Sie, daß das Feld 'swapfilename' einen Null-terminierten
  384.    String ohne Längenbyte, keinen Pascal-String, enthält.
  385. <}
  386.  
  387. type
  388.    prep_block = record
  389.                   xmm: longint;           {e XMM entry address }
  390.                                           {d Einsprungadresse XMM }
  391.                   first_mcb: integer;     {e Segment of first MCB }
  392.                                           {d Segment des ersten MCB }
  393.                   psp_mcb: integer;       {e Segment of MCB of our PSP }
  394.                                           {d Segment des MCB unseres PSP }
  395.                   env_mcb: integer;       {e MCB of Environment segment }
  396.                                           {d MCB des Umgebungsvariablenblocks }
  397.                   noswap_mcb: integer;    {e MCB that may not be swapped }
  398.                                           {d MCB der nicht Ausgelagert wird }
  399.                   ems_pageframe: integer; {e EMS page frame address }
  400.                                           {d EMS-Seiten-Adresse }
  401.                   handle: integer;        {e EMS/XMS/File handle }
  402.                                           {d Handle für EMS/XMS/Datei }
  403.                   total_mcbs: integer;    {e Total number of MCBs }
  404.                                           {d Gesamtzahl MCBs }
  405.                   swapmethod: byte;       {e Method for swapping }
  406.                                           {d Auslagerungsmethode }
  407.                   swapfilename: array [0..80] of char; 
  408.                                           {e Swap file name if swapping to file }
  409.                                           {d Auslagerungsdateiname }
  410.                   end;
  411.  
  412. var
  413.    swap_prep: prep_block;
  414.  
  415. { ------------------------------------------------------------------------- }
  416.  
  417. procedure putenv (envvar: string);
  418. {  Adds a string to the environment. Note that the change to the
  419.    environment is valid for an exec'ed process only, and only if you
  420.    set the 'newenv' parameter in do_exec to TRUE. }
  421.  
  422.  
  423. function envcount: integer;
  424. function envstr (index: integer): string;
  425. function getenv (envvar: string): string;
  426.  
  427. { Replacement functions for the environment handling functions in the
  428.   DOS unit. All three functions work exactly like their DOS-unit
  429.   counterparts, except that they recognize the changes to the child
  430.   environment produced by 'putenv'. }
  431.  
  432.  
  433.  
  434. {===========================================================================}
  435.  
  436. Implementation
  437.  
  438. {>e
  439.    Define REDIRECT to support redirection.
  440.    CAUTION: The definition in 'spawn.asm' must match this definition!!
  441. <}
  442. {>d
  443.    Definieren Sie REDIRECT um Dateiumleitung zu untertützen.
  444.    ACHTUNG: Die Definition in 'spawn.asm' muß mit dieser Definition 
  445.    übereinstimmen!!
  446. <}
  447.  
  448. {$DEFINE REDIRECT}
  449.  
  450. const
  451.    swap_filename = '$$AAAAAA.AAA';
  452.  
  453.    {e internal flags for prep_swap }
  454.    {d interne Flags für prep_swap }
  455.  
  456.    CREAT_TEMP      = $0080;
  457.    DONT_SWAP_ENV   = $4000;
  458.  
  459.    ERR_COMSPEC     = -7;
  460.    ERR_NOMEM       = -8;
  461.  
  462.    spaces: set of #9..' ' = [#9, ' '];
  463.  
  464. type
  465.    stringptr = ^string;
  466.    stringarray = array [0..10000] of stringptr;
  467.    stringarrptr = ^stringarray;
  468.    bytearray = array [0..30000] of byte;
  469.    bytearrayptr = ^bytearray;
  470.  
  471. var
  472.    envptr: stringarrptr;   { Pointer to the changed environment }
  473.    envcnt: integer;        { Count of environment strings }
  474.    cmdpath: string;
  475.    cmdpars: string;
  476.    drive: string [3];
  477.    dir: string [67];
  478.    name: string [9];
  479.    ext: string [5];
  480.  
  481.  
  482. {$L spawnp}
  483. function do_spawn (swapping: integer;
  484.                    var xeqfn; var cmdtail; envlen: word;
  485.                    var env
  486. {$IFDEF REDIRECT}
  487.                    ;stdin: pstring; stdout: pstring; stderr: pstring
  488. {$ENDIF}
  489.                    ): integer; external;
  490.  
  491. function prep_swap (method: integer; var swapfn): integer; external;
  492.  
  493.  
  494. { helper routine }
  495.  
  496. function strpbrk (par, pattern: string): integer;
  497.    { find position of any one of the characters in 'pattern' in string 'par' }
  498.    var
  499.       i: integer;
  500.    begin
  501.    for i := 1 to length (par) do
  502.       if pos (par [i], pattern) > 0
  503.          then begin
  504.          strpbrk := i;
  505.          exit;
  506.          end;
  507.    strpbrk := 0;
  508.    end;
  509.      
  510. { Environment routines }
  511.  
  512. function envcount: integer;
  513.  
  514.    { Returns count of strings in environment. }
  515.  
  516.    var
  517.       cnt: integer;
  518.    begin
  519.    if envptr = nil { If not yet changed }
  520.       then envcount := dos.envcount
  521.       else envcount := envcnt;
  522.    end;
  523.  
  524.  
  525. function envstr (index: integer): string;
  526.  
  527.    { Returns environment string 'index' }
  528.  
  529.    begin
  530.    if envptr = nil { If not yet changed }
  531.       then envstr := dos.envstr (index)
  532.       else if (index <= 0) or (index >= envcnt)
  533.       then envstr := ''
  534.       else if envptr^ [index - 1] = nil
  535.       then envstr := ''
  536.       else envstr := envptr^ [index - 1]^;
  537.    end;
  538.  
  539.  
  540. function name_eq (var n1, n2: string): boolean;
  541.  
  542.    { Compares search string 'n1' with environment string 'n2'.
  543.      Case is insignificant. }
  544.  
  545.    var
  546.       i: integer;
  547.       eq: boolean;
  548.    begin
  549.    i := 1;
  550.    eq := false;
  551.    while (i <= length (n1)) and (i <= length (n2)) and
  552.          (upcase (n1 [i]) = upcase (n2 [i])) do
  553.       i := i + 1;
  554.    name_eq := (i > length (n1)) and (i <= length (n2)) and (n2 [i] = '=');
  555.    end;
  556.  
  557.  
  558. function searchenv (var str: string): integer;
  559.  
  560.    { Search for environment string, returns index in 'envptr' array.
  561.      Assumes 'envptr' is not NIL. }
  562.  
  563.    var
  564.       idx: integer;
  565.       found: boolean;
  566.    begin
  567.    idx := 0;
  568.    found := false;
  569.  
  570.    while (idx < envcnt) and not found do
  571.       begin
  572.       if envptr^ [idx] <> nil
  573.          then found := name_eq (str, envptr^ [idx]^);
  574.       idx := idx + 1;
  575.       end;
  576.    if not found
  577.       then searchenv := -1
  578.       else searchenv := idx - 1;
  579.    end;
  580.  
  581.  
  582. function getenv (envvar: string): string;
  583.  
  584.    { Returns value of environment string specified by name. }
  585.  
  586.    var
  587.       strp: stringptr;
  588.       eq: integer;
  589.    begin
  590.    if envptr = nil { If not yet changed }
  591.       then getenv := dos.getenv (envvar)
  592.       else begin
  593.       eq := searchenv (envvar);
  594.       if eq < 0
  595.          then getenv := ''
  596.          else begin
  597.          strp := envptr^ [eq];
  598.          eq := pos ('=', strp^);
  599.          getenv := copy (strp^, eq + 1, length (strp^) - eq);
  600.          end;
  601.       end;
  602.    end;
  603.  
  604.  
  605. procedure init_envptr;
  606.  
  607.    { Initialise 'envptr' array. Called when 'putenv' is used for the
  608.      first time. Copies all environment strings into heap storage,
  609.      and builds an array of pointers to this strings. }
  610.  
  611.    var
  612.       i: integer;
  613.       str: string [255];
  614.    begin
  615.    envcnt := dos.envcount;
  616.    getmem (envptr, envcnt * sizeof (stringptr));
  617.    if envptr = nil
  618.       then exit;
  619.    for i := 0 to envcnt - 1 do
  620.       begin
  621.       str := dos.envstr (i + 1);
  622.       getmem (envptr^ [i], length (str) + 1);
  623.       if envptr^ [i] <> nil
  624.          then envptr^ [i]^ := str;
  625.       end;
  626.    end;
  627.  
  628.  
  629. procedure putenv (envvar: string);
  630.  
  631.    { Adds the string 'envvar' to the environment, or changes the
  632.      environment string if the name is already present. }
  633.  
  634.    var
  635.       idx, eq: integer;
  636.       help: stringarrptr;
  637.       tmpvar : string;
  638.    begin
  639.    if envptr = nil
  640.       then init_envptr;
  641.    if envptr = nil
  642.       then exit;
  643.  
  644.    eq := pos ('=', envvar);
  645.    if eq = 0
  646.       then exit;
  647.    for idx := 1 to eq do
  648.       envvar [idx] := upcase (envvar [idx]);
  649.    tmpvar := copy (envvar, 1, eq - 1); { Copy the portion up to "=" }
  650.  
  651.    idx := searchenv (tmpvar);
  652.    if idx >= 0
  653.       then begin
  654.       freemem (envptr^ [idx], length (envptr^ [idx]^) + 1);
  655.  
  656.       if eq >= length (envvar)
  657.          then envptr^ [idx] := nil
  658.          else begin
  659.          getmem (envptr^ [idx], length (envvar) + 1);
  660.          if envptr^ [idx] <> nil
  661.             then envptr^ [idx]^ := envvar;
  662.          end;
  663.       end
  664.       else if eq < length (envvar)
  665.       then begin
  666.       getmem (help, (envcnt + 1) * sizeof (stringptr));
  667.       if help = nil
  668.          then exit;
  669.       move (envptr^, help^, envcnt * sizeof (stringptr));
  670.       freemem (envptr, envcnt * sizeof (stringptr));
  671.       envptr := help;
  672.       getmem (envptr^ [envcnt], length (envvar) + 1);
  673.       if envptr^ [envcnt] <> nil
  674.          then envptr^ [envcnt]^ := envvar;
  675.       envcnt := envcnt + 1;
  676.       end;
  677.    end;
  678.  
  679.  
  680.  
  681. { Routines to search for files }
  682.  
  683. function tryext (var fn: string): integer;
  684.  
  685.    { Try '.COM', '.EXE', and '.BAT' on current filename, modify filename if found. }
  686.  
  687.    var
  688.       nfn: filename;
  689.       ok: boolean;
  690.    begin
  691.    tryext := 1;
  692.    nfn := fn + '.COM';
  693.    ok := exists (nfn);
  694.    if not ok
  695.       then begin
  696.       nfn := fn + '.EXE';
  697.       ok := exists (nfn);
  698.       end;
  699.    if not ok
  700.       then begin
  701.       tryext := 2;
  702.       nfn := fn + '.BAT';
  703.       ok := exists (nfn);
  704.       end;
  705.    if not ok
  706.       then tryext := 0
  707.       else fn := nfn;
  708.    end;
  709.  
  710.  
  711. function findfile (var fn: string): integer;
  712.  
  713.    { Try to find the file 'fn' in the current path. Modifies the filename
  714.      accordingly. }
  715.  
  716.    var
  717.       path: string;
  718.       i, j: integer;
  719.       hasext, found, check: integer;
  720.    begin
  721.    if fn = ''
  722.       then begin
  723.       if cmdpath = ''
  724.          then findfile := ERR_COMSPEC
  725.          else findfile := 3;
  726.       exit;
  727.       end;
  728.  
  729.    check := checkpath (fn, INF_NODIR, drive, dir, name, ext, fn);
  730.    if check < 0
  731.       then begin
  732.       findfile := check;
  733.       exit;
  734.       end;
  735.  
  736.    if ((check and HAS_WILD) <> 0) or ((check and HAS_FNAME) = 0)
  737.       then begin
  738.       findfile := ERR_FNAME;
  739.       exit;
  740.       end;
  741.  
  742.    if (check and HAS_EXT) <> 0
  743.       then begin
  744.       for i := 1 to length (ext) do
  745.          ext [i] := upcase (ext [i]);
  746.       if ext = '.BAT'
  747.          then hasext := 2
  748.          else hasext := 1;
  749.       end
  750.       else hasext := 0;
  751.  
  752.    if hasext <> 0
  753.       then begin
  754.       if (check and FILE_EXISTS) <> 0
  755.          then found := hasext
  756.          else found := 0;
  757.       end
  758.       else found := tryext (fn);
  759.  
  760.    if (found <> 0) or ((check and (HAS_PATH or HAS_DRIVE)) <> 0)
  761.       then begin
  762.       findfile := found;
  763.       exit;
  764.       end;
  765.  
  766.    path := getenv ('PATH');
  767.    i := 1;
  768.    while (found = 0) and (i <= length (path)) do
  769.       begin
  770.       j := 0;
  771.       while (path [i] <> ';') and (i <= length (path)) do
  772.          begin
  773.          j := j + 1;
  774.          fn [j] := path [i];
  775.          i := i + 1;
  776.          end;
  777.       i := i + 1;
  778.       if (j > 0)
  779.          then begin
  780.          if not (fn [j] in ['\', '/'])
  781.             then begin
  782.             j := j + 1;
  783.             fn [j] := '\';
  784.             end;
  785.          fn [0] := chr (j);
  786.          fn := fn + name + ext;
  787.          check := checkpath (fn, INF_NODIR, drive, dir, name, ext, fn);
  788.          if hasext <> 0
  789.             then begin
  790.             if (check and FILE_EXISTS) <> 0
  791.                then found := hasext
  792.                else found := 0;
  793.             end
  794.             else found := tryext (fn);
  795.          end;
  796.       end;
  797.    findfile := found;
  798.    end; { findfile }
  799.  
  800.  
  801. {>e 
  802.    Get name and path of the command processor via the COMSPEC
  803.    environmnt variable. Any parameters after the program name
  804.    are copied and inserted into the command line.
  805. <}
  806. {>d
  807.    Namen und Pfad des Kommandoprozessors über die COMSPEC-Umgebungs-
  808.    Variable bestimmen. Parameter nach dem Programmnamen werden kopiert
  809.    und in die Kommandozeile eingefügt.
  810. <}
  811.  
  812. procedure getcmdpath;
  813.    var
  814.       i, found: integer;
  815.    begin
  816.    if length (cmdpath) > 0
  817.       then exit;
  818.    cmdpath := getenv ('COMSPEC');
  819.    cmdpars := '';
  820.    found := 0;
  821.  
  822.    if cmdpath <> ''
  823.       then begin
  824.       i := 1;
  825.       while (i <= length (cmdpath)) and (cmdpath [i] in spaces) do
  826.          inc (i);
  827.       if i > 1
  828.          then begin
  829.          cmdpath := copy (cmdpath, i, 255);
  830.          i := 1;
  831.          end;
  832.  
  833.       i := strpbrk (cmdpath, ';,=+/"[]|<> '#9);
  834.       if i <> 0
  835.          then begin
  836.          cmdpars := copy (cmdpath, i, 128);
  837.          cmdpath [0] := chr (i - 1);
  838.          i := 1;
  839.          while (i <= length (cmdpars)) and (cmdpars [i] in spaces) do
  840.             inc (i);
  841.          if i > 1
  842.             then cmdpars := copy (cmdpars, i, 128);
  843.          if cmdpars <> ''
  844.             then cmdpars := cmdpars + ' ';
  845.          end;
  846.       found := findfile (cmdpath);
  847.       end;
  848.  
  849.    if found = 0
  850.       then begin
  851.       cmdpath := 'COMMAND.COM';
  852.       cmdpars := '';
  853.       found := findfile (cmdpath);
  854.       if found = 0
  855.          then cmdpath := '';
  856.       end;
  857.    end;
  858.  
  859.  
  860. function tempdir (var outfn: filename): boolean;
  861.  
  862.    { Set temporary file path.
  863.      Read "TMP/TEMP" environment. If empty or invalid, clear path.
  864.      If TEMP is drive or drive+backslash only, return TEMP.
  865.      Otherwise check if given path is a valid directory.
  866.    }
  867.    var
  868.       stmp: array [0..3] of filename;
  869.       i, res: integer;
  870.  
  871.    begin
  872.    stmp [0] := getenv ('TMP');
  873.    stmp [1] := getenv ('TEMP');
  874.    stmp [2] := '.\';
  875.    stmp [3] := '\';
  876.  
  877.    for i := 0 to 3 do
  878.       if length (stmp [i]) <> 0
  879.          then begin
  880.          outfn := stmp [i];
  881.          res := checkpath (outfn, 0, drive, dir, name, ext, outfn);
  882.          if (res > 0) and ((res and IS_DIR) <> 0) and ((res and IS_READ_ONLY) = 0)
  883.             then begin
  884.             tempdir := true;
  885.             exit;
  886.             end;
  887.          end;
  888.    tempdir := false;
  889.    end;
  890.  
  891.  
  892. {$IFDEF REDIRECT}
  893.  
  894. function parse_redirect (var par: string; idx: integer;
  895.                          var stdin, stdout, stderr: pstring): boolean;
  896.    var
  897.       ch: char;
  898.       fnp: pstring;
  899.       fn: string;
  900.       app, i, beg, fne: integer;
  901.  
  902.    begin
  903.    i := idx;
  904.    par [length (par) + 1] := #0;
  905.  
  906.    repeat
  907.       app := 0;
  908.       ch := par [i];
  909.       beg := i;
  910.       i := i + 1;
  911.       if ch <> '<'
  912.          then begin
  913.          if par [i] = '&'
  914.             then begin
  915.             ch := '&';
  916.             inc (i);
  917.             end;
  918.          if par [i] = '>'
  919.             then begin
  920.             app := 1;
  921.             inc (i);
  922.             end;
  923.          end;
  924.  
  925.       while (i <= length (par)) and (par [i] in spaces) do
  926.          inc (i);
  927.       fn := copy (par, i, 255);
  928.       fne := strpbrk (fn, ';,=+/"[]|<> '#9);
  929.       if fne = 0
  930.          then fne := length (fn) + 1;
  931.       par := copy (par, 1, beg - 1) + copy (fn, fne, 255);
  932.       i := beg;
  933.       fn [0] := chr (fne - 1);
  934.       if (fne = 0) or (length (fn) = 0)
  935.          then begin
  936.          parse_redirect := false;
  937.          exit;
  938.          end;
  939.       
  940.       getmem (fnp, length (fn) + app + 2);
  941.       if fnp = NIL
  942.          then begin
  943.          parse_redirect := false;
  944.          exit;
  945.          end;
  946.       if app <> 0
  947.          then fnp^ := '>' + fn
  948.          else fnp^ := fn;
  949.       fnp^ [length (fnp^) + 1] := #0;
  950.  
  951.       case ch of
  952.          '<':  if stdin <> NIL
  953.                   then begin
  954.                   parse_redirect := false;
  955.                   exit;
  956.                   end
  957.                else stdin := fnp;
  958.  
  959.          '>':  if stdout <> NIL
  960.                   then begin
  961.                   parse_redirect := false;
  962.                   exit;
  963.                   end
  964.                else stdout := fnp;
  965.  
  966.          '&':  if stderr <> NIL
  967.                   then begin
  968.                   parse_redirect := false;
  969.                   exit;
  970.                   end
  971.                else stderr := fnp;
  972.          end;
  973.  
  974.       i := strpbrk (fn, '<>');
  975.    until (i <= 0);
  976.  
  977.    par [length (par) + 1] := #0;
  978.    parse_redirect := true;
  979.    end;
  980.  
  981. {$ENDIF}
  982.  
  983.  
  984. function do_exec (xfn: string; pars: string; spawn: integer;
  985.                   needed: word; newenv: boolean): integer;
  986.    label
  987.       exit;
  988.    var
  989.       cmdbat: integer;
  990.       swapfn: filename;
  991.       avail: word;
  992.       regs: registers;
  993.       envlen, einx: word;
  994.       idx, len, rc: integer;
  995.       envp: bytearrayptr;
  996.       swapping: integer;
  997. {$IFDEF REDIRECT}
  998.       stdin, stdout, stderr: pstring;
  999. {$ENDIF}
  1000.    begin
  1001. {$IFDEF REDIRECT}
  1002.    stdin := NIL; stdout := NIL; stderr := NIL;
  1003. {$ENDIF}
  1004.  
  1005.    getcmdpath;
  1006.    envlen := 0;
  1007.  
  1008.    { First, check if the file to execute exists. }
  1009.  
  1010.    cmdbat := findfile (xfn);
  1011.    if cmdbat <= 0
  1012.       then begin
  1013.       do_exec := RC_NOFILE or -cmdbat;
  1014.       goto exit;
  1015.       end;
  1016.  
  1017.    if cmdbat > 1   { COMMAND.COM or Batch file }
  1018.       then begin
  1019.       if length (cmdpath) = 0
  1020.          then begin
  1021.          do_exec := RC_NOFILE or -ERR_COMSPEC;
  1022.          goto exit;
  1023.          end;
  1024.  
  1025.       if cmdbat = 2
  1026.          then pars := cmdpars + '/c ' + xfn + ' ' + pars
  1027.          else pars := cmdpars + pars;
  1028.       xfn := cmdpath;
  1029.       end;
  1030.  
  1031. {$IFDEF REDIRECT}
  1032.    idx := strpbrk (pars, '<>');
  1033.    if idx > 0
  1034.       then if not parse_redirect (pars, idx, stdin, stdout, stderr)
  1035.          then begin
  1036.          do_exec := RC_REDIRERR;
  1037.          goto exit;
  1038.          end;
  1039. {$ENDIF}
  1040.  
  1041.    { Now create a copy of the environment if the user wants it, and
  1042.      if the environment has been changed. }
  1043.  
  1044.    if newenv and (envptr <> nil)
  1045.       then begin
  1046.       for idx := 0 to envcnt - 1 do
  1047.          envlen := envlen + length (envptr^ [idx]^) + 1;
  1048.       if envlen > 0
  1049.          then begin
  1050.          envlen := envlen + 1;
  1051.          getmem (envp, envlen);
  1052.          if envp = nil
  1053.             then begin
  1054.             do_exec := RC_ENVERR;
  1055.             goto exit;
  1056.             end;
  1057.          einx := 0;
  1058.          for idx := 0 to envcnt - 1 do
  1059.             begin
  1060.             len := length (envptr^ [idx]^);
  1061.             move (envptr^ [idx]^ [1], envp^ [einx], len);
  1062.             envp^ [einx + len] := 0;
  1063.             einx := einx + len + 1;
  1064.             end;
  1065.          envp^ [einx] := 0;
  1066.          end;
  1067.       end;
  1068.  
  1069.    if spawn = 0
  1070.       then swapping := -1
  1071.       else begin
  1072.  
  1073.       { Determine amount of free memory }
  1074.       with regs do
  1075.          begin
  1076.          ax := $4800;
  1077.          bx := $ffff;
  1078.          msdos (regs);
  1079.          avail := regs.bx;
  1080.          end;
  1081.  
  1082.       { No swapping if available memory > needed }
  1083.  
  1084.       if needed < avail
  1085.          then swapping := 0
  1086.          else begin
  1087.  
  1088.          { Swapping necessary, use 'TMP' or 'TEMP' environment variable
  1089.            to determine swap file path if defined. }
  1090.  
  1091.          swapping := spawn;
  1092.          if (spawn and USE_FILE) <> 0
  1093.             then begin
  1094.             if not tempdir (swapfn)
  1095.                then begin
  1096.                spawn := spawn xor USE_FILE;
  1097.                swapping := spawn;
  1098.                end
  1099.                else begin
  1100.                if (dosversion and $ff) >= 3
  1101.                   then swapping := swapping or CREAT_TEMP
  1102.                   else begin
  1103.                   swapfn := swapfn + swap_filename;
  1104.                   len := length (swapfn);
  1105.                   while exists (swapfn) do
  1106.                      begin
  1107.                       if (swapfn [len] >= 'Z')
  1108.                         then len := len - 1;
  1109.                       if (swapfn [len] = '.')
  1110.                         then len := len - 1;
  1111.                       swapfn [len] := succ (swapfn [len]);
  1112.                       end;
  1113.                   end;
  1114.                swapfn [length (swapfn) + 1] := #0;
  1115.                end;
  1116.             end;
  1117.          end;
  1118.       end;
  1119.  
  1120.    { All set up, ready to go. }
  1121.  
  1122.    if swapping > 0
  1123.       then begin
  1124.       if envlen = 0
  1125.          then swapping := swapping or DONT_SWAP_ENV;
  1126.  
  1127.       rc := prep_swap (swapping, swapfn);
  1128.       if rc < 0
  1129.          then begin
  1130.          do_exec := RC_PREPERR or -rc;
  1131.          goto exit;
  1132.          end;
  1133.       end;
  1134.  
  1135.    xfn [length (xfn) + 1] := #0;
  1136.    pars [length (pars) + 1] := #0;
  1137.  
  1138.    if @spawn_check <> NIL
  1139.       then begin
  1140.       rc := spawn_check (cmdbat, swapping, xfn, pars);
  1141.       if rc <> 0
  1142.          then begin
  1143.          do_exec := rc;
  1144.          goto exit;
  1145.          end;
  1146.       end;
  1147.  
  1148.    swapvectors;
  1149. {$IFDEF REDIRECT}
  1150.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^, stdin, stdout, stderr);
  1151. {$ELSE}
  1152.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^);
  1153. {$ENDIF}
  1154.    swapvectors;
  1155.  
  1156.    { Free the environment buffer if it was allocated. }
  1157.  
  1158. exit:
  1159.    if envlen > 0
  1160.       then freemem (envp, envlen);
  1161. {$IFDEF REDIRECT}
  1162.    if stdin <> NIL
  1163.       then freemem (stdin, length (stdin^) + 2);
  1164.    if stdout <> NIL
  1165.       then freemem (stdout, length (stdout^) + 2);
  1166.    if stderr <> NIL
  1167.       then freemem (stderr, length (stderr^) + 2);
  1168. {$ENDIF}
  1169.    end;
  1170.  
  1171.  
  1172. { Initialisation for environment processing }
  1173.  
  1174. Begin
  1175. envptr := nil;
  1176. envcnt := 0;
  1177. cmdpath := '';
  1178. @spawn_check := nil;
  1179. End.
  1180.  
  1181. 
  1182.